home *** CD-ROM | disk | FTP | other *** search
/ PC World Komputer 2010 April / PCWorld0410.iso / hity wydania / Ubuntu 9.10 PL / karmelkowy-koliberek-desktop-9.10-i386-PL.iso / casper / filesystem.squashfs / usr / share / pyshared / jockey / handlers.py < prev    next >
Text File  |  2009-09-21  |  22KB  |  649 lines

  1. # (c) 2007 Canonical Ltd.
  2. #
  3. # This program is free software; you can redistribute it and/or modify
  4. # it under the terms of the GNU General Public License as published by
  5. # the Free Software Foundation; either version 2 of the License, or
  6. # (at your option) any later version.
  7. #
  8. # This program is distributed in the hope that it will be useful,
  9. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  10. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  11. # GNU General Public License for more details.
  12. #
  13. # You should have received a copy of the GNU General Public License along
  14. # with this program; if not, write to the Free Software Foundation, Inc.,
  15. # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
  16.  
  17. '''Define some common abstract basic handler types.
  18.  
  19. These provide the common functionality for concrete handlers of different
  20. classes, like handlers for a kernel module, a driver package, a handler group,
  21. etc.
  22.  
  23. Custom concrete handlers need to fulfill the following requirements:
  24.  - __init__(self, backend) must take exactly one argument (a reference to a
  25.    Backend instance). All othe properties must be detected by the
  26.    constructor or changed with methods. These classes are instantiated
  27.    automatically, which is not possible with constructors which need more
  28.    arguments.
  29.  
  30.  - All handler types in this module have some abstract functions which need to
  31.    be implemented (see the documentation of the particular classes).
  32. '''
  33.  
  34. import subprocess, os.path, sys, logging
  35. from gettext import gettext as _
  36.  
  37. import detection
  38. from oslib import OSLib
  39.  
  40. #--------------------------------------------------------------------#
  41.  
  42. class Handler:
  43.     '''Abstract basic handler.'''
  44.  
  45.     def __init__(self, backend, name, description=None, rationale=None):
  46.         '''Create a handler with given (human readable) name.
  47.         
  48.         Every handler should have a human readable name. A custom rationale and
  49.         a multi-line description can be given, too. Every handler gets a
  50.         reference to the currently used Backend so that it can request
  51.         installation of packages and other system changes.
  52.  
  53.         By default, available handlers are announced in
  54.         AbstractUI.check(). If you want to have a handler which is
  55.         available, but not announced that way, set self.annonuce to False.
  56.         '''
  57.         self._hwids = [] # covered HardwareIDs
  58.         self._changed = False
  59.         self.backend = backend
  60.  
  61.         self._name = name
  62.         self._description = description
  63.         self._rationale = rationale
  64.         self.license = None
  65.         self.announce = True
  66.  
  67.         # the following properties are not specified in the ctor, since they
  68.         # might be changed after instantiation;
  69.         # subclass ctors might set that before calling us
  70.         if not hasattr(self, '_free'):
  71.             self._free = None
  72.         if hasattr(self, 'package') and self.package:
  73.             self._package_defaults()
  74.         else:
  75.             self.package = None
  76.         self.driver_vendor = None
  77.         self.version = None
  78.         self.repository = None
  79.         self._recommended = False
  80.  
  81.     def _package_defaults(self):
  82.         '''Set fallback name/description/freeness from package.'''
  83.  
  84.         if self.package and (not self._name or self._description is None):
  85.             (distro_name, distro_desc) = OSLib.inst.package_description(self.package)
  86.             if not self._name:
  87.                 self._name = distro_name
  88.             if self._description is None:
  89.                 self._description = distro_desc
  90.         if self.package and self._free is None:
  91.             try:
  92.                 self._free = OSLib.inst.is_package_free(self.package)
  93.             except (KeyError, ValueError):
  94.                 # we cannot determine it right now
  95.                 pass
  96.  
  97.     def name(self):
  98.         '''Return one-line name of the handler (for human consumption).'''
  99.  
  100.         self._package_defaults()
  101.         return self._name
  102.  
  103.     def description(self):
  104.         '''Return multi-line description of the handler.'''
  105.  
  106.         self._package_defaults()
  107.         return self._description
  108.  
  109.     def id(self):
  110.         '''Return an unique identifier of the handler.
  111.  
  112.         This is used for specifying a handler with --enable/--disable on the
  113.         command line, and is mentioned in the --list output.
  114.         '''
  115.         if self.package:
  116.             i = 'pkg:' + self.package
  117.         else:
  118.             i = '%s:%s' % (str(self.__class__).split('.')[-1], self.name())
  119.         if self.driver_vendor:
  120.             i += ':' + self.driver_vendor.replace(' ', '_')
  121.         return i
  122.  
  123.     def rationale(self):
  124.         '''Return rationale as to why this driver might be enabled.
  125.         
  126.         Might return None if no rationale is available.
  127.         '''
  128.         return self._rationale
  129.  
  130.     def changed(self):
  131.         '''Return if the module has been enabled/disabled at least once.'''
  132.  
  133.         return self._changed
  134.  
  135.     #
  136.     # The following methods can be specialized in subclasses
  137.     # 
  138.  
  139.     def can_change(self):
  140.         '''Check whether we can actually modify settings of this handler.
  141.  
  142.         This might not be the case if e. g. the user manually modified a
  143.         configuration file. Return an explanatory text if settings can not be
  144.         changed, or None if changing is ok.
  145.         '''
  146.         return None
  147.  
  148.     def __str__(self):
  149.         return '%s([%s, %s, %s] %s)' % (
  150.             self.id(),
  151.             str(self.__class__).split('.')[-1],
  152.             self.free() and 'free' or 'nonfree',
  153.             self.enabled() and 'enabled' or 'disabled',
  154.             self.name())
  155.  
  156.     #
  157.     # The following methods must be implemented in subclasses
  158.     # 
  159.  
  160.     def free(self):
  161.         '''Return if the handler represents a free software driver.'''
  162.  
  163.         if self._free is not None:
  164.             return self._free
  165.         else:
  166.             raise NotImplementedError, 'subclasses need to implement this'
  167.  
  168.     def enabled(self):
  169.         '''Return if the handler is enabled.
  170.         
  171.         'Enabled' means that the user agreed to use this driver if it is
  172.         applicable.
  173.         '''
  174.         if self.package:
  175.             return OSLib.inst.package_installed(self.package)
  176.         else:
  177.             return True
  178.  
  179.     def used(self):
  180.         '''Return if the handler is currently in use.'''
  181.  
  182.         raise NotImplementedError, 'subclasses need to implement this'
  183.  
  184.     def recommended(self):
  185.         '''Return if the version of a certain driver is recommended over others
  186.         when more than one driver flavour supports the same device.
  187.         
  188.         This method should return True only for the recommended version while
  189.         it will return False for any other compatible version. If only one
  190.         version of a driver is provided, then it should return False.
  191.         '''
  192.         return self._recommended
  193.     
  194.     def available(self):
  195.         '''Return if the conditions to use this handler on the system are met.
  196.  
  197.         This usually means that the hardware for this driver is available, but
  198.         there might be hardware independent drivers, too.
  199.         
  200.         If this returns True or False, the answer is definitive and no further
  201.         detection, db querying, etc is performed. If this returns None, then
  202.         the handler cannot decide availability on its own; in that case it is
  203.         merely available in the handler pool, and an external driver database
  204.         (detection.DriverDB) is queried.
  205.         '''
  206.         if self.package:
  207.             if not self.repository or OSLib.inst.repository_enabled(self.repository):
  208.                 try:
  209.                     OSLib.inst.package_description(self.package)
  210.                     return None
  211.                 except ValueError:
  212.                     return False
  213.             else:
  214.                 return None # undecidable until the repo is added
  215.         else:
  216.             raise NotImplementedError, 'subclasses need to implement this'
  217.  
  218.     def enable(self):
  219.         '''Allow the OS to use it if the hardware is available.
  220.         
  221.         If possible, the handler should be loaded, too. Return True if
  222.         immediately successful, or False if the system needs to be rebooted for
  223.         the changes to become effective.
  224.         '''
  225.         if self.repository:
  226.             OSLib.inst.add_repository(self.repository)
  227.  
  228.         if self.package:
  229.             self.backend.install_package(self.package)
  230.             if not OSLib.inst.package_installed(self.package):
  231.                 # do not touch _changed if package failed to install
  232.                 return True
  233.         # packages might install/remove blacklists
  234.         OSLib.inst._load_module_blacklist()
  235.         self._changed = True
  236.         return True
  237.  
  238.     def disable(self):
  239.         '''Prevent the OS from using it even if the hardware is available.
  240.  
  241.         If possible, the handler should be unloaded, too. Return True if
  242.         immediately successful, or False if the system needs to be rebooted for
  243.         the changes to become effective.
  244.  
  245.         '''
  246.         if self.repository:
  247.             OSLib.inst.remove_repository(self.repository)
  248.  
  249.         if self.package:
  250.             self.backend.remove_package(self.package)
  251.             if OSLib.inst.package_installed(self.package):
  252.                 # do not touch _changed if package failed to remove
  253.                 return
  254.         self._changed = True
  255.  
  256.         return True
  257.  
  258. #--------------------------------------------------------------------#
  259.  
  260. class HandlerGroup(Handler):
  261.     '''Perform operations on a group of handlers.
  262.  
  263.     A group should be provided if it makes little sense to present several very
  264.     similar handlers in the UI. For example, the three VMWare or the dozens of
  265.     commercial OSS drivers should be grouped.
  266.     '''
  267.     def __init__(self, backend, name, id, description=None, rationale=None):
  268.         Handler.__init__(self, backend, name, description, rationale)
  269.         self._id = id
  270.         self.subhandlers = []
  271.  
  272.     def id(self):
  273.         '''Return an unique identifier of the handler.'''
  274.  
  275.         return self._id
  276.  
  277.     def add(self, handler):
  278.         '''Add a subhandler.'''
  279.  
  280.         self.subhandlers.append(handler)
  281.  
  282.     def free(self):
  283.         '''Return if all subhandlers represent free software drivers.'''
  284.  
  285.         for h in self.subhandlers:
  286.             if not h.free():
  287.                 return False
  288.  
  289.         return True
  290.  
  291.     def enabled(self):
  292.         '''Return if all subhandlers are enabled.'''
  293.  
  294.         for h in self.subhandlers:
  295.             if not h.enabled():
  296.                 return False
  297.  
  298.         return True
  299.  
  300.     def used(self):
  301.         '''Return if any subhandler is used.'''
  302.  
  303.         for h in self.subhandlers:
  304.             if h.used():
  305.                 return True
  306.  
  307.         return False
  308.  
  309.     def available(self):
  310.         '''Return if the hardware for any subhandler is available.
  311.         
  312.         If all subhandlers return False, this returns False. If any subhandler
  313.         returns True, this returns True. Otherwise this returns None.
  314.         '''
  315.         all_false = True
  316.  
  317.         for h in self.subhandlers:
  318.             a = h.available()
  319.             if a:
  320.                 return True
  321.             if a == None:
  322.                 all_false = False
  323.             else:
  324.                 assert a == False
  325.  
  326.         if all_false:
  327.             return False
  328.         else:
  329.             return None
  330.  
  331.     def enable(self):
  332.         '''Enable all subhandlers.'''
  333.  
  334.         result = True
  335.         for h in self.subhandlers:
  336.             result = h.enable() and result
  337.         return result
  338.  
  339.     def disable(self):
  340.         '''Disable all subhandlers.'''
  341.  
  342.         result = True
  343.         for h in self.subhandlers:
  344.             result = h.disable() and result
  345.         return result
  346.  
  347.     def changed(self):
  348.         '''Return if at least one subhandler has been enabled/disabled at
  349.         least once.'''
  350.  
  351.         for h in self.subhandlers:
  352.             if h.changed():
  353.                 return True
  354.  
  355.         return False
  356.  
  357.     def can_change(self):
  358.         '''Check whether we can actually modify settings of this handler.'''
  359.  
  360.         assert self.subhandlers
  361.  
  362.         for h in self.subhandlers:
  363.             c = h.can_change()
  364.             if c:
  365.                 return c
  366.  
  367.         return None
  368.  
  369. #--------------------------------------------------------------------#
  370.  
  371. class KernelModuleHandler(Handler):
  372.     '''Handler for a kernel module.
  373.     
  374.     This class can be used as a standard handler for kernel modules (and in
  375.     fact detection.get_handlers() uses this as a default handler if there is no
  376.     custom one). Subclasses have to implement __init__() at least.
  377.     '''
  378.     _loaded_modules = None
  379.     
  380.     def __init__(self, backend, kernel_module, name=None, description=None, rationale=None):
  381.         '''Create handler for a kernel module.
  382.         
  383.         If not given explicitly, the name is read from modinfo's 'description'
  384.         field.
  385.         '''
  386.         self.module = kernel_module
  387.         self._modinfo = detection.get_modinfo(self.module)
  388.         if not name:
  389.             assert self._modinfo, 'kernel module %s exists' % self.module
  390.             name = '\n'.join(self._modinfo.get('description', [self.module]))
  391.         Handler.__init__(self, backend, name, description, rationale)
  392.         self._do_rebind = True
  393.  
  394.     def id(self):
  395.         '''Return an unique identifier of the handler.'''
  396.  
  397.         i = 'kmod:' + self.module
  398.         if self.driver_vendor:
  399.             i += ':' + self.driver_vendor.replace(' ', '_')
  400.         return i
  401.  
  402.     def free(self):
  403.         '''Return if the handler represents a free software driver.'''
  404.  
  405.         # this function needs to be kept in sync with the kernel function
  406.         # is_license_gpl_compatible()
  407.  
  408.         if self._free is not None:
  409.             return self._free
  410.  
  411.         assert self._modinfo, 'kernel module %s exists' % self.module
  412.         for l in self._modinfo.get('license', ['unknown']):
  413.             if l in ('GPL', 'GPL v2', 'GPL and additional rights', 
  414.                 'Dual BSD/GPL', 'Dual MIT/GPL', 'Dual MPL/GPL', 'BSD'):
  415.                 return True
  416.         return False
  417.  
  418.     def enabled(self):
  419.         '''Return if the handler is enabled.
  420.         
  421.         'Enabled' means that the user agreed to use this driver if it is
  422.         applicable.
  423.         '''
  424.         return not OSLib.inst.module_blacklisted(self.module) and \
  425.             (self._modinfo is not None) and Handler.enabled(self)
  426.  
  427.     def used(self):
  428.         '''Return if the handler is currently in use.'''
  429.  
  430.         return self.module_loaded(self.module) and (self.package is None or
  431.             OSLib.inst.package_installed(self.package))
  432.  
  433.     def available(self):
  434.         '''Return if the conditions to use this handler on the system are met
  435.         (e. g. hardware for this driver is available).
  436.  
  437.         This defaults to None, because we usually want to delegate this to the
  438.         driver db. Subclasses are welcome to override this, of course.
  439.         '''
  440.         # check for unavailable package, etc.
  441.         try:
  442.             if Handler.available(self) == False:
  443.                 return False
  444.         except NotImplementedError:
  445.             pass
  446.         return None
  447.  
  448.     def enable(self):
  449.         '''Allow the OS to use it if the hardware is available.
  450.         
  451.         This removes the module from the modprobe blacklist.
  452.         '''
  453.         Handler.enable(self)
  454.         OSLib.inst.blacklist_module(self.module, False)
  455.         subprocess.call([OSLib.inst.modprobe_path, self.module])
  456.         self._modinfo = detection.get_modinfo(self.module)
  457.         self.read_loaded_modules()
  458.         if self._do_rebind:
  459.             return self.rebind(self.module)
  460.  
  461.     def disable(self):
  462.         '''Prevent the OS from using it even if the hardware is available.
  463.  
  464.         This adds the module to the modprobe blacklist.
  465.         '''
  466.         Handler.disable(self)
  467.         OSLib.inst.blacklist_module(self.module, True)
  468.         self._modinfo = detection.get_modinfo(self.module)
  469.         return False # TODO: can we make this automatic?
  470.  
  471.     @classmethod
  472.     def rebind(klass, module):
  473.         '''Re-bind all devices using the module.
  474.         
  475.         This is necessary for example to reload firmware. Return True on
  476.         success, or False if rebind failed for any device.
  477.         '''
  478.         drivers_dir = os.path.join(OSLib.inst.sys_dir, 'module', module, 'drivers')
  479.         if not os.path.isdir(drivers_dir):
  480.             logging.warning('%s does not exist, cannot rebind %s driver' % (
  481.                 drivers_dir, module))
  482.             return
  483.  
  484.         succeeded = True
  485.  
  486.         for driver in os.listdir(drivers_dir):
  487.             driver_path = os.path.join(drivers_dir, driver)
  488.             for device in os.listdir(driver_path):
  489.                 # only consider subdirs which are not called 'module'
  490.                 if device == 'module' or not os.path.isdir(
  491.                     os.path.join(driver_path, device)):
  492.                     continue
  493.                 try:
  494.                     logging.debug('unbind/rebind on driver %s: device %s', driver_path, device)
  495.                     f = open(os.path.join(driver_path, 'unbind'), 'w')
  496.                     f.write(device)
  497.                     f.close()
  498.                     f = open(os.path.join(driver_path, 'bind'), 'w')
  499.                     f.write(device)
  500.                     f.close()
  501.                 except IOError:
  502.                     logging.warning('unbind/rebind for device %s on driver %s failed', 
  503.                         device, driver_path, exc_info=True)
  504.                     succeeded = False
  505.  
  506.         return succeeded
  507.  
  508.     @classmethod
  509.     def read_loaded_modules(klass):
  510.         '''Get the list of loaded kernel modules.'''
  511.  
  512.         klass._loaded_modules = []
  513.  
  514.         proc_modules = open(OSLib.inst.proc_modules)
  515.         try:
  516.             for line in proc_modules:
  517.                 try:
  518.                     line = line[:line.index(' ')]
  519.                 except ValueError:
  520.                     pass
  521.  
  522.                 klass._loaded_modules.append(line.strip())
  523.         finally:
  524.             proc_modules.close()
  525.  
  526.     @classmethod
  527.     def module_loaded(klass, module):
  528.         '''Return if a module is currently loaded.'''
  529.  
  530.         if klass._loaded_modules == None:
  531.             klass.read_loaded_modules()
  532.  
  533.         return module in klass._loaded_modules
  534.  
  535. #--------------------------------------------------------------------#
  536.  
  537. class FirmwareHandler(KernelModuleHandler):
  538.     '''Handler for an already available kernel module needing firmware.
  539.  
  540.     Subclasses need to extend enable() and implement disable() to do something
  541.     with the downloaded file (unpack it, put into the right directory, etc.).
  542.     This class' enable() function will deal with downloading it and the UI
  543.     progress reporting of the download.
  544.     '''
  545.     def __init__(self, backend, kernel_module, testfile, name=None, description=None, 
  546.             rationale=None, url=None, sha1sum=None, free=False):
  547.         '''Create handler for a piece of firmware for a kernel module.
  548.         
  549.         The required argument 'url' specifies where the firmware can be
  550.         downloaded from. The optional 'sha1sum' argument provides a checksum of
  551.         the downloaded file. The file will not be installed if it does not
  552.         match.
  553.  
  554.         enabled() will return True iff the path in testfile exists.
  555.  
  556.         By default this handler assumes that the firmware is not free (since
  557.         otherwise the distribution could ship it together with the driver). You
  558.         can set 'free' to True for free firmware or to None to use the kernel
  559.         module's freeness.
  560.     
  561.         If not given explicitly, the name is read from modinfo's 'description'
  562.         field.
  563.         '''
  564.         self.url = url
  565.         self.sha1sum = sha1sum
  566.         self._free = free
  567.         self.testfile = testfile
  568.  
  569.         KernelModuleHandler.__init__(self, backend, kernel_module, name,
  570.             description, rationale)
  571.  
  572.     def id(self):
  573.         '''Return an unique identifier of the handler.'''
  574.  
  575.         i = 'firmware:' + self.module
  576.         if self.driver_vendor:
  577.             i += ':' + self.driver_vendor.replace(' ', '_')
  578.         return i
  579.  
  580.     def free(self):
  581.         '''Return if the handler represents a free software driver.'''
  582.  
  583.         if self._free is None:
  584.             return KernelModuleHandler.free(self)
  585.         return self._free
  586.  
  587.     def enabled(self):
  588.         '''Return if the handler is enabled.
  589.         
  590.         'Enabled' means that the user agreed to use this driver if it is
  591.         applicable.
  592.         '''
  593.         return os.path.exists(self.testfile) and KernelModuleHandler.enabled(self)
  594.  
  595.     def used(self):
  596.         '''Return if the handler is currently in use.'''
  597.  
  598.         return self.enabled() and KernelModuleHandler.used(self)
  599.  
  600.     def enable(self):
  601.         '''Allow the OS to use it if the hardware is available.
  602.         
  603.         This downloads the url and puts it into self.firmware_file. Subclasses
  604.         need to provide an actual implementation what to do with the file.
  605.         '''
  606.         raise NotImplementedError, 'FirmwareHandler is currently not implemented'
  607.         #self.firmware_file = self.ui.download_url(self.url)[0]
  608.         #if not self.firmware_file:
  609.         #    return
  610.  
  611.         # TODO: sha1sum check
  612.  
  613.         KernelModuleHandler.enable(self)
  614.  
  615.     def disable(self):
  616.         '''Prevent the OS from using it even if the hardware is available.
  617.         
  618.         Implementation in subclasses need to remove the firmware files and call
  619.         KernelModuleHandler.disable().
  620.         '''
  621.         raise NotImplementedError, 'subclasses need to implement this'
  622.  
  623. #--------------------------------------------------------------------#
  624.  
  625. class PrinterDriverHandler(Handler):
  626.     '''Handler for a printer driver.'''
  627.  
  628.     def id(self):
  629.         '''Return an unique identifier of the handler.
  630.  
  631.         This is used for specifying a handler with --enable/--disable on the
  632.         command line, and is mentioned in the --list output.
  633.         '''
  634.         if self.package:
  635.             i = 'printer:' + self.package
  636.         else:
  637.             i = 'printer:%s' % self.name()
  638.         if self.version:
  639.             i += ':' + str(self.version)
  640.         elif self.driver_vendor:
  641.             i += ':' + self.driver_vendor.replace(' ', '_')
  642.         return i
  643.  
  644.     def used(self):
  645.         '''Return if the handler is currently in use.'''
  646.  
  647.         # TODO: query cups for actually using the driver
  648.         return self.enabled()
  649.